// Copyright (c) 2025 Yao Yu
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

#![deny(unsafe_code)]

use ostd::cpu::context::UserContext;
use ostd::prelude::println;
use ostd::mm::VmSpace;

mod fork;
mod fs;
pub mod ipc;
use fork::*;
use fs::sys_write;
use ipc::*;
use crate::{context::Context, cpu::SyscallArgs, error::Error, prelude::*};

struct SyscallArguments {
    syscall_id: u64,
    args: [u64; 6],
}
impl SyscallArguments {
    fn new_from_uctx(user_ctx: &UserContext) -> Self {
        let syscall_id = user_ctx.syscall_num() as u64;
        let syscall_args = user_ctx.syscall_args().map(|x| x as u64);
        Self {
            syscall_id,
            args: syscall_args,
        }
    }
}

/// This macro is used to define syscall handler.
/// The first param is the number of parameters,
/// The second param is the function name of syscall handler,
/// The third is optional, means the args(if parameter number > 0),
/// The fourth is optional, means if cpu ctx is required.
macro_rules! syscall_handler {
    (0, $fn_name: ident, $args: ident, $ctx: expr) => {
        $fn_name($ctx)
    };
    (0, $fn_name: ident, $args: ident, $ctx: expr, $user_ctx: expr) => {
        $fn_name($ctx, $user_ctx)
    };

    (1, $fn_name: ident, $args: ident, $ctx: expr) => {
        $fn_name($args[0] as _, $ctx)
    };
    (1, $fn_name: ident, $args: ident, $ctx: expr, $user_ctx: expr) => {
        $fn_name($args[0] as _, $ctx, $user_ctx)
    };

    (2, $fn_name: ident, $args: ident, $ctx: expr) => {
        $fn_name($args[0] as _, $args[1] as _, $ctx)
    };
    (2, $fn_name: ident, $args: ident, $ctx: expr, $user_ctx: expr) => {
        $fn_name($args[0] as _, $args[1] as _, $ctx, $user_ctx)
    };

    (3, $fn_name: ident, $args: ident, $ctx: expr) => {
        $fn_name($args[0] as _, $args[1] as _, $args[2] as _, $ctx)
    };
    (3, $fn_name: ident, $args: ident, $ctx: expr, $user_ctx: expr) => {
        $fn_name($args[0] as _, $args[1] as _, $args[2] as _, $ctx, $user_ctx)
    };

    (4, $fn_name: ident, $args: ident, $ctx: expr) => {
        $fn_name(
            $args[0] as _,
            $args[1] as _,
            $args[2] as _,
            $args[3] as _,
            $ctx,
        )
    };
    (4, $fn_name: ident, $args: ident, $ctx: expr, $user_ctx: expr) => {
        $fn_name(
            $args[0] as _,
            $args[1] as _,
            $args[2] as _,
            $args[3] as _,
            $ctx,
            $user_ctx,
        )
    };

    (5, $fn_name: ident, $args: ident, $ctx: expr) => {
        $fn_name(
            $args[0] as _,
            $args[1] as _,
            $args[2] as _,
            $args[3] as _,
            $args[4] as _,
            $ctx,
        )
    };
    (5, $fn_name: ident, $args: ident, $ctx: expr, $user_ctx: expr) => {
        $fn_name(
            $args[0] as _,
            $args[1] as _,
            $args[2] as _,
            $args[3] as _,
            $args[4] as _,
            $ctx,
            $user_ctx,
        )
    };

    (6, $fn_name: ident, $args: ident, $ctx: expr) => {
        $fn_name(
            $args[0] as _,
            $args[1] as _,
            $args[2] as _,
            $args[3] as _,
            $args[4] as _,
            $args[5] as _,
            $ctx,
        )
    };
    (6, $fn_name: ident, $args: ident, $ctx: expr, $user_ctx: expr) => {
        $fn_name(
            $args[0] as _,
            $args[1] as _,
            $args[2] as _,
            $args[3] as _,
            $args[4] as _,
            $args[5] as _,
            $ctx,
            $user_ctx,
        )
    };
}

macro_rules! dispatch_fn_inner {
    ( $args: ident, $ctx: ident, $user_ctx: ident, $handler: ident ( args[ .. $cnt: tt ] ) ) => {
        $crate::syscall::syscall_handler!($cnt, $handler, $args, $ctx)
    };
    ( $args: ident, $ctx: ident, $user_ctx: ident, $handler: ident ( args[ .. $cnt: tt ] , &user_ctx ) ) => {
        $crate::syscall::syscall_handler!($cnt, $handler, $args, $ctx, &$user_ctx)
    };
    ( $args: ident, $ctx: ident, $user_ctx: ident, $handler: ident ( args[ .. $cnt: tt ] , &mut user_ctx ) ) => {
        // `$user_ctx` is already of type `&mut ostd::cpu::UserContext`,
        // so no need to take `&mut` again
        $crate::syscall::syscall_handler!($cnt, $handler, $args, $ctx, $user_ctx)
    };
}

macro_rules! impl_syscall_dispatcher {
    ( $( $name: ident = $value: literal => $func: ident $args: tt );* $(;)? ) => {
        // 定义公开syscall_id常量
        $(
            pub const $name: u64 = $value;
        )*

        pub fn syscall_dispatcher(
            syscall_number: u64,
            args: [u64; 6],
            user_context: &mut UserContext,
            ctx: &crate::context::Context,
        ) -> $crate::prelude::Result<isize> {
            match syscall_number {
                $(
                    $value => {
                        // $func(args[0] as _, args[1] as _, args[2] as _, user_context, vm_space)
                        // $func(args[0] as _, args[1] as _, args[2] as _, vm_space)
                        // $func(args[0] as _, args[1] as _, args[2] as _, ctx)
                        $crate::syscall::dispatch_fn_inner!(args, ctx, user_context, $func $args)
                    }
                )*
                _ => {
                    // TODO: error handle
                    // TODO: error message
                    Err($crate::error::Error::new($crate::error::Errno::ENOSYS))
                }
            }
        }
    }
}

// use crate::syscall::impl_syscall_dispatcher;
impl_syscall_dispatcher! {
    SYSCALL_WRITE = 1       => sys_write(args[..3]);
    SYSCALL_EXIT = 60       => sys_exit(args[..1]);
    // SYSCALL_SEND = 61       => sys_send(args[..3]);
    // SYSCALL_RECEIVE = 62    => sys_receive(args[..3]);
    // SYSCALL_NOTIFY = 63     => sys_notify(args[..0]);
}

use dispatch_fn_inner;
use syscall_handler;

pub fn handle_syscall(context: &Context, user_context: &mut UserContext) {
    let syscall_args = SyscallArguments::new_from_uctx(user_context);
    let args = syscall_args.args;
    let syscall_number= syscall_args.syscall_id;
    let syscall_ret = syscall_dispatcher(syscall_number as u64, args, user_context, context);
    match syscall_ret {
        Ok(ret) => {
            user_context.set_syscall_ret(ret as usize);
        },
        Err(e) => {
            let errno = e.errno();
            user_context.set_syscall_ret((-errno) as usize)
        }
    }
}

fn sys_exit(exit_code: i32, context: &Context) -> Result<isize> {
    println!("[kernel] application exited with code {}", exit_code);
    let current_thread = context.thread();
    // TODO: 这里只是退出线程，未来需要将进程也一起退出。
    current_thread.set_exit();
    Ok(0)
}